package io.hepu.robotize.service.impl;

import com.google.common.collect.ImmutableMap;
import io.hepu.robotize.IConstant;
import io.hepu.robotize.IContext;
import io.hepu.robotize.auth.JRedis;
import io.hepu.robotize.auth.Jwts;
import io.hepu.robotize.auth.Shiros;
import io.hepu.robotize.dto.ActiveUser;
import io.hepu.robotize.enums.AuthType;
import io.hepu.robotize.enums.IStatus;
import io.hepu.robotize.exception.AuthGrantException;
import io.hepu.robotize.model.Permission;
import io.hepu.robotize.model.Role;
import io.hepu.robotize.model.User;
import io.hepu.robotize.security.AuthGrantGuard;
import io.hepu.robotize.service.IPermissionService;
import io.hepu.robotize.service.IRoleService;
import io.hepu.robotize.service.ISysService;
import io.hepu.robotize.service.IUserService;
import org.apache.shiro.util.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

@Service
@Transactional
public class SysService implements ISysService {
    private static final Logger LOG = LoggerFactory.getLogger(ISysService.class);

    @Resource
    private IRoleService roleService;
    @Resource
    private IUserService userService;
    @Resource
    private IPermissionService permissionService;

    @Override
    public List<Permission> findPermsByUserId(String userId) {
        return permissionService.findByUserId(userId);
    }

    @Override
    public List<Role> findRolesByUserId(String userId) {
        return roleService.findByUserId(userId);
    }

    @Override
    public User findUserById(String userId) {
        return userService.findOne(userId);
    }

    /**
     * 系统用户JWT登录验证
     *
     * @param userName 用户名字
     * @param password 验证密码
     * @return JWT token
     */
    @Override
    public String localAuth(String userName, String password, long timestamp) {
        Assert.notNull(userName, "用户名不能为空");
        Assert.notNull(password, "密码不能为空");
        ActiveUser user = userService.findActUserByName(userName, AuthType.LOCAL);
        AuthGrantGuard.verify(user);
        /*--------------------------------------
        |           开 始 登 录 校 验            |
        =======================================*/
        String encrypt = Shiros.encrypt(password, user.getCipher());
        if (user.getCredential().equals(encrypt)) {
            return buildAuthentication(user, AuthType.LOCAL, timestamp);
        } else {
            throw AuthGrantException.of(IStatus.AUTH_BAD_CREDENTIALS);
        }
    }

    /**
     * 短信验证登录
     *
     * @param mobile    手机号码
     * @param timestamp 当前时间戳
     * @return JWT token
     */
    @Override
    public String smsAuth(String mobile, long timestamp) {
        Assert.notNull(mobile, "手机号不能为空");
        User user = userService.findByMobile(mobile);
        AuthGrantGuard.verify(user);
        return buildAuthentication(user, AuthType.SMS, timestamp);
    }

    /**
     * 创建认证信息
     *
     * @param user      用户对象
     * @param timestamp 当前时间戳
     * @return JWT token
     */
    private String buildAuthentication(User user, AuthType authType, long timestamp) {
        LOG.debug("[::: Login ::: {}", user);
        /*-----------------------
         *  1. 登录则清除旧缓存
         *  2. 重新保存凭证失效时间
         *  3. 获取用户角色信息
         *  4. 生成签名并返回
         ========================*/
        String shiroCacheKey = IContext.getShiroCacheKey(user.getId());
        if (JRedis.exists(shiroCacheKey)) {
            JRedis.delete(shiroCacheKey);
        }
        // 缓存Token失效时间
        String jwtRefreshKey = IContext.getJwtRefreshKey(user.getId());
        JRedis.serialize(jwtRefreshKey, timestamp, IConstant.JWT_REFRESH_TOKEN_EXPIRATION);

        // 组装Claims
        Map<String, Object> payload = ImmutableMap.of(
                IConstant.CLAIM_AUTH_TYPE, authType.name(),
                IConstant.CLAIM_AUTH_PLATFORM, IContext.getAuthzPlatform(),
                IConstant.CLAIM_SIGNING_AT, timestamp);

        // 生成用户签名
        String jwtToken = Jwts.sign(user.getId(), payload, timestamp);

        // 缓存访问Token
        String jwtCacheKey = IContext.getJwtCacheKey(user.getId());
        JRedis.serialize(jwtCacheKey, jwtToken, IConstant.JWT_ACCESS_TOKEN_EXPIRATION);

        return jwtToken;
    }

}
